package edu.gatech.fiveminutesleft.storage;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.util.Log;

/**
 * Stores a table of all known storage locations indexed by URI.
 * 
 * Contains 0..* Storage objects.
 * Contains 0..1 LocalStorage objects.
 * Contains 0..1 Context objects.
 * 
 * The purpose of this class is to act as a router between all known storage
 * locations, so that stored objects may be addressed uniformly even though
 * they may be in different storage locations.
 * 
 * As a result, Storage objects are not interfaced with directly; all lookups
 * go through this class.
 * 
 * @author Nick Johnson
 */
public class StorageManager {

	/**
	 * Table of all known storage locations.
	 */
	private static Map<String, Storage>	storageLocations;

	/**
	 * Currently-set local storage location.
	 */
	private static LocalStorage			localStorage;

	/**
	 * Currently-set context for Android local resources.
	 */
	private static Context				activeContext;

	/**
	 * Flush all storage locations' buffers.
	 */
	public static void flushAll() throws IOException {

		if (storageLocations != null) {
			for (Storage s : storageLocations.values()) {
				s.flush();
			}
		}
	}

	/**
	 * Register a new storage location in the table.
	 * 
	 * @param s
	 *            Storage location.
	 */
	public static void add(Storage s) {

		if (storageLocations == null) {
			// construct table
			storageLocations = new HashMap<String, Storage>();
		}

		// add storage location to table
		storageLocations.put(s.getServerAddress(), s);
	}

	/**
	 * Set the current local storage location. This should not be done after initialization.
	 * 
	 * @param l
	 *            Local storage location to register.
	 */
	public static void setLocalStorage(LocalStorage l) {
		localStorage = l;
	}

	/**
	 * Get the storage location with the given URI.
	 * 
	 * @param uri
	 *            URI of storage location.
	 * @return A storage location matching this URI on success, null on error.
	 */
	static Storage get(String uri) {

		if (storageLocations == null) {
			// no storage locations -> storage location cannot be found
			return null;
		}

		// TODO make some effort to find the location based on its URI

		// look up storage location by URL
		return storageLocations.get(uri);
	}

	/**
	 * Get the current local storage location.
	 * 
	 * @return The current local storage location.
	 */
	public static LocalStorage getLocalStorage() {
		return localStorage;
	}

	/**
	 * Get a task reference by fully dereferencing a storage address. This is the intended mechanism by which storage
	 * addresses are turned into active storage references. Using Storage.getTask() directly from non-storage is wrong.
	 * 
	 * @param addr
	 *            Storage address to locate.
	 * @return A reference to the addressed task on success, null on error.
	 */
	public static TaskReference getTask(StorageAddress addr) {
		Storage s = StorageManager.get(addr.getServerAddress());

		if (s == null) {
			return null;
		}

		return s.getTask(addr);
	}

	/**
	 * Get a task list reference by fully dereferencing a storage address. This is the intended mechanism by which storage
	 * addresses are turned into active storage references. Using Storage.getTaskList() directly from non-storage code is
	 * wrong.
	 * 
	 * @param addr
	 *            Storage address to locate.
	 * @return A reference to the addressed task list on success, null on error.
	 */
	public static TaskListReference getTaskList(StorageAddress addr) {
		Storage s = StorageManager.get(addr.getServerAddress());
		
		if (s == null) {
			return null;
		}

		return s.getTaskList(addr);
	}

	/**
	 * Get an account reference by username from the local storage location. This is the intended mechanism by which
	 * usernames are turned into active account references. Using LocalStorage.getAccount() directly from non-storage code is
	 * wrong.
	 * 
	 * @param username
	 *            Username of account to look up.
	 * @return A reference to the found account on success, null on error.
	 */
	public static AccountReference getAccount(String username) {

		if (localStorage == null) {
			Log.d("StorageManager.getAccount", "no local storage location is registered");
			return null;
		}

		return StorageManager.localStorage.getAccount(username);
	}

	/**
	 * Create an account by username in the local storage location. If an account with the given username already exists, it
	 * is simply looked up instead of being created or replaced.
	 * 
	 * @param username
	 *            Username of account to create.
	 * @return A reference to the new account on success, null on error.
	 */
	public static AccountReference createAccount(String username) {

		if (localStorage == null) {
			Log.d("StorageManager.createAccount", "no local storage location is registered");
			return null;
		}

		return StorageManager.localStorage.createAccount(username);
	}

	/**
	 * Get an email reference by email from the local storage location. This is the intended mechanism by which
	 * email addresses are turned into active email references. Using LocalStorage.getEmail() directly from non-storage
	 * code is wrong.
	 *
	 * @param email
	 *            Email address of email structure to look up.
	 * @return A reference to the found email on success, null on error.
	 */
	public static EmailReference getEmail(String email) {

		if (localStorage == null) {
			Log.d("StorageManager.createAccount", "no local storage location is registered");
			return null;
		}

		return StorageManager.localStorage.getEmail(email);
	}

	/**
	 * Create an email by email address in the local storage location. If an email with the given email address already
	 * exists, it is simply looked up instead of being created or replaced.
	 * 
	 * @param email
	 *            Email address of email to create.
	 * @return A reference to the new email on success, null on error.
	 */
	public static EmailReference createEmail(String email) {

		if (localStorage == null) {
			Log.d("StorageManager.createAccount", "no local storage location is registered");
			return null;
		}

		return StorageManager.localStorage.createEmail(email);
	}

	/**
	 * Set the active storage context.
	 * 
	 * @param c
	 *            The new active storage context.
	 */
	public static void setActiveContext(Context c) {
		activeContext = c;
	}

	/**
	 * @return The active storage context.
	 */
	public static Context getActiveContext() {
		return activeContext;
	}

}
